use cmsis_rtos2_rs::static_mem::MutexValue;
use proto_bytes::ProtoEncode2;

use super::{
    draw_water_task,
    driver::{
        ctrl, motor, nozzle_motor, sensor,
        temperature::{self, Temp},
    },
    steam_heat_task, water_heat_task, HeatErr,
};

const VERSION: u16 = 0x19;

static TEMP: MutexValue<Result<Temp, HeatErr>> = MutexValue::uninit(Err(HeatErr::Temp));

pub unsafe fn init() {
    TEMP.init_once("temp");
}

pub fn temp() -> Result<Temp, HeatErr> {
    *TEMP.lock()
}

struct Status {
    temp: Temp,
    sensor: u16,
    adc: u16,
    ctrl_flag: u16,
    flow_count: u16,
    draw_state: u8,
    water_heat_state: u8,
    steam_heat_state: u8,
}

impl ProtoEncode2 for Status {
    unsafe fn encode(self, ptr: *mut u8) -> usize {
        VERSION.encode(ptr);
        self.temp.water.encode(ptr.add(2));
        self.temp.steam.encode(ptr.add(4));
        self.sensor.encode(ptr.add(6));
        self.adc.encode(ptr.add(8));
        self.ctrl_flag.encode(ptr.add(10));
        self.flow_count.encode(ptr.add(12));
        self.draw_state.encode(ptr.add(14));
        self.water_heat_state.encode(ptr.add(15));
        self.steam_heat_state.encode(ptr.add(16));
        17
    }
}

pub fn collect() -> impl ProtoEncode2 {
    let temp = temperature::read();
    *TEMP.lock() = temp;

    let temp_info = temp.unwrap_or_default();

    let sen = (sensor::is_pearl_checked() as u16)
        | ((sensor::is_nozzle_origin() as u16) << 1)
        | ((sensor::is_stir_top() as u16) << 2)
        | ((sensor::is_stir_bottom() as u16) << 3)
        | ((sensor::is_trun_left() as u16) << 4)
        | ((sensor::is_trun_right() as u16) << 5)
        | ((sensor::is_clamp_left() as u16) << 6)
        | ((sensor::is_clamp_right() as u16) << 7)
        | ((nozzle_motor::is_alm() as u16) << 8)
        | ((sensor::is_water_high() as u16) << 9)
        | ((sensor::is_water_low() as u16) << 10)
        | ((temp.is_err() as u16) << 11);

    let analog = motor::analog_value();
    let ctrl_flag = ctrl::flag_value();
    let flow_count = sensor::flow_count();

    Status {
        temp: temp_info,
        sensor: sen,
        adc: analog,
        ctrl_flag,
        flow_count,
        draw_state: draw_water_task::state().into(),
        water_heat_state: water_heat_task::state().into(),
        steam_heat_state: steam_heat_task::state().into(),
    }
}
